#!/usr/bin/python3 -u

import argparse
import json
import tempfile
import glob
import os
import subprocess
import textwrap
import sys
import uuid

RELEASE_IMAGE_TEMPLATE = "registry.svc.ci.openshift.org/ocp/release"


def install_oc():
    with tempfile.TemporaryDirectory() as output_dir:
        # TODO: Use urllib3
        # Fetch oc tar
        cmd = ['curl', '-LO', 'https://mirror.openshift.com/pub/openshift-v4/clients/oc/latest/linux/oc.tar.gz']
        try:
            print(f"Running {' '.join(cmd)}")
            subprocess.check_call(cmd, cwd=output_dir)
        except subprocess.CalledProcessError as extract_err:
            print(f"Failed to extract oc! Error: {extract_err}")
            raise

        # Untar oc client
        try:
            cmd = ['sudo', 'tar', 'xzf', os.path.join(output_dir, "oc.tar.gz"), '-C', '/usr/local/bin']
            print(f"Running {' '.join(cmd)}")
            subprocess.check_call(cmd, cwd=output_dir)
        except Exception as tar_err:
            print(f"Failed to unpack oc! Error: {tar_err}")
            raise tar_err


def prepare_release_image(meta, build, release_image_prefix, stream):
    oscontainer_image = meta["oscontainer"]["image"]
    oscontainer_digest = meta["oscontainer"]["digest"]

    release_image = f"{release_image_prefix}:{build}"

    auth_file = os.environ.get("REGISTRY_AUTH_FILE")
    auth_file_params = ['-a', auth_file]
    if auth_file is None:
        auth_file_params = []

    # TODO: unhardcode this
    # Prepare a new release
    cmd = ['oc', 'adm', 'release', 'new'] + auth_file_params + [
           '-n', 'ocp', '--server', 'https://api.ci.openshift.org',
           '--from-image-stream', stream,
           '--to-image', release_image,
           f"machine-os-content={oscontainer_image}@{oscontainer_digest}"
    ]
    try:
        print(f"Running {' '.join(cmd)}")
        subprocess.check_call(cmd)
    except Exception as release_err:
        print(f"Failed to create new release payload! Error: {release_err}")
        raise release_err

    return release_image


# Run test openshift build
def run_openshift_install(region, secret_dir, release_image, buildID, log_level='debug'):
    install_exception = None

    pull_secret_path = os.path.join(secret_dir, "pull_secret.json")

    with open(pull_secret_path) as f:
        pull_secret = f.read()

    with open(os.path.join(secret_dir, "ssh-publickey")) as f:
        ssh_publickey = f.read()

    # Create temp directory to store installer output
    with tempfile.TemporaryDirectory() as output_dir:
        # Write install-config.yaml
        install_yaml = f"""\
        apiVersion: v1beta4
        baseDomain: devcluster.openshift.com
        clusterID: {uuid.uuid1()}
        metadata:
            name: rhcos-test-{buildID}
        networking:
            networkType: OpenshiftSDN
        platform:
            aws:
                region: {region}
        pullSecret: |
            {pull_secret}
        sshKey: |
            {ssh_publickey}
        """
        install_yaml_file_path = os.path.join(output_dir, "install-config.yaml")
        with open(install_yaml_file_path, 'wt', encoding='utf-8') as f:
            f.write(textwrap.dedent(install_yaml))

        # Extract installer from release image
        try:
            cmd = [
                '/usr/local/bin/oc', 'adm', 'release', 'extract',
                '-a', pull_secret_path,
                '--tools', release_image]
            print(f"Running {' '.join(cmd)}")
            subprocess.check_call(cmd, cwd=output_dir)
        except subprocess.CalledProcessError as extract_err:
            print(f"Failed to extract installer! Error: {extract_err}")
            raise extract_err

        install_archive_list = glob.glob(os.path.join(output_dir, 'openshift-install-linux-*.tar.gz'))
        if len(install_archive_list) == 0:
            raise Exception("No installer archive found")

        # Untar the installer
        try:
            cmd = ['sudo', 'tar', 'xzf', install_archive_list[0], '-C', '/usr/local/bin']
            print(f"Running {' '.join(cmd)}")
            subprocess.check_call(cmd, cwd=output_dir)
        except Exception as tar_err:
            print(f"Failed to extract oc! Error: {tar_err}")
            raise tar_err

        # Create cluster
        cmd = [
            'env',
            f'AWS_SHARED_CREDENTIALS_FILE={os.path.join(secret_dir, "aws-credentials")}',
            f'OPENSHIFT_INSTALL_RELEASE_IMAGE_OVERRIDE={release_image}',
            '/usr/local/bin/openshift-install',
            '--log-level=debug',
            f"--dir={output_dir}"
        ]
        try:
            create_cmd = cmd + ['create', 'cluster']
            print(f"Running {' '.join(create_cmd)}")
            subprocess.check_call(create_cmd, cwd=output_dir)
        except subprocess.CalledProcessError as create_err:
            print(f"Failed to create cluster! Error: {create_err}")
            if create_err is not None:
                install_exception = create_err

        # Destroy cluster regardless of the error
        try:
            destroy_cmd = cmd + ['destroy', 'cluster']
            print(f"Running {' '.join(destroy_cmd)}")
            subprocess.check_call(destroy_cmd, cwd=output_dir)
        except subprocess.CalledProcessError as destroy_err:
            print(f"Failed to deprovision cluster! Error: {destroy_err}")
            if destroy_err is not None:
                install_exception = destroy_err

        if install_exception:
            raise install_exception


def main():
    # parse args and dispatch
    parser = argparse.ArgumentParser()
    parser.add_argument("--build", action='store', required=True)
    parser.add_argument("--region", action='store', required=True)
    parser.add_argument("--secrets", action='store', required=True)
    parser.add_argument("--release-image", action='store', required=True)
    parser.add_argument("--stream", action='store', required=True)
    args = parser.parse_args()

    with open(f"builds/{args.build}/x86_64/meta.json") as meta_f:
        meta = json.load(meta_f)

    try:
        print("Running a test OpenShift install")
        install_oc()
        new_release_image = prepare_release_image(meta, args.build, args.release_image, args.stream)
        run_openshift_install(args.region, args.secrets, new_release_image, args.build)
    except subprocess.CalledProcessError:
        sys.exit(1)


if __name__ == '__main__':
    main()
